package rig.com.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import rig.com.model.dto.ActivityDTO;
import rig.com.model.mapper.ActivityMapper;
import rig.com.service.ActivityService;
import rig.com.util.RedisUtils;

import java.math.BigDecimal;


/**
 * @author Rig
 * @create 2021/9/22 15:13
 **/
@Service
public class IActivityServiceImpl extends ServiceImpl<ActivityMapper, ActivityDTO> implements ActivityService {

    @Autowired
    RedisUtils redisUtils;

    /*****
     * Redis hash 活动信息前缀
     */
    private final String HASH_ACTIVITY_ID = "hash:activity-";

    /****
     * Redis string 锁前缀
     */
    private final String INIT_ACTIVITY = "init:init-activity-";

    /*****
     * Redis hash 参与用户信息前缀
     */
    private final String HASH_USER = "hash:activity_user-";

    /****
     * Redis set集合 非活动前缀，减少缓存压力
     */
    private final String SET_UNACTIVITY = "set:unactivity";

    /****
     * TTL 过期因子
     */
    private final long SET_UNACTIVITY_FACTOR = 4;

    /***
     * TTL 默认过期时间
     */
    private final long TIME_OUT = 5;

    /******
     * 库存字段
     */
    private final String STOCK = "stock";

    @Override
    public Boolean quick(Long activityId, String jwtToken) {
        //校验token
        if (!StringUtils.hasText(jwtToken)) {
            return false;
        }
        //hash key
        String activityHashKey = HASH_ACTIVITY_ID + activityId;
        //校验redis
        if (redisUtils.hasKey(activityHashKey)) {
            //存在
            //判断活动的有效性
            if (!this.checkActivityIsValid(activityHashKey, activityId)) {
                return false;
            }
            if (redisUtils.hincr(activityHashKey, STOCK, -1L) < 0) {
                return false;
            }
            //秒杀逻辑开始
            //TODO 支付流程判断，字段pay_status
            //TODO 加入mq死信队列，做订单超时回滚,topic: unpaid_order
            //TODO -mysql -redis -mq 幂等性问题
        } else {
            //不存在
            //加锁创建
            if (initActivity(activityId)) {
                this.quick(activityId, jwtToken);
            } else {
                return false;
            }
        }
        return false;
    }

    @Override
    public ActivityDTO queryById(Long id) {
        QueryWrapper<ActivityDTO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        return this.getOne(queryWrapper);
    }

    /********
     * @description 判断活动是否开始
     * @param activityHashKey hash表的key
     * @return true - 成功 ； false - 失败
     */
    private boolean checkActivityIsValid(String activityHashKey, Long activityId) {
        //用户HASH表
        String userHashkey = HASH_USER + activityId;
        ActivityDTO activityDTO = BeanUtil.toBean(redisUtils.hmget(activityHashKey), ActivityDTO.class);
        long startTime = activityDTO.getStartTime().getTime();
        long endTime = activityDTO.getEndTime().getTime();
        long nowTime = System.currentTimeMillis();
        if (nowTime > startTime && nowTime < endTime) {
            if (activityDTO.getOnly().equals(1)) {
                //只能参与一次
                return !redisUtils.hHashKey(activityHashKey, userHashkey);
            } else {
                return true;
            }
        }
        return false;
    }

    /******
     * @description 加锁初始化活动以及相对应长度队列 FIFO（RIGHT PUSH，LEFT POP）
     * @param activityId 活动id
     * @return true - 成功 ； false - 失败
     */
    @Override
    public boolean initActivity(Long activityId) {
        //当前活动 - 锁key
        //TODO 锁的key应该由请求标识作为唯一标识，当前key有可能会被其他客户端释放掉
        String initActivityId = INIT_ACTIVITY + activityId;
        //hash key
        String activityHashKey = HASH_ACTIVITY_ID + activityId;
        //校验是否是无效活动id
        if (redisUtils.sismember(SET_UNACTIVITY, activityId)) {
            return false;
        }
        //不存在，加锁，在库里查询
        //锁 TIME_OUT 秒后释放(释放时间由具体业务做调整)
        if (redisUtils.setIfAbsent(initActivityId, activityId, TIME_OUT)) {
            //获取到锁
            QueryWrapper<ActivityDTO> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("id", activityId);
            ActivityDTO one = this.getOne(queryWrapper);
            if (ObjectUtils.isEmpty(one)) {
                //缓存不存在的activityId，减少校验
                redisUtils.sadd(SET_UNACTIVITY, TIME_OUT << SET_UNACTIVITY_FACTOR, activityId);
                redisUtils.del(initActivityId);
                return false;
            }
            BigDecimal nowTime = new BigDecimal(System.currentTimeMillis());
            BigDecimal endTime = new BigDecimal(one.getEndTime().getTime());
            //是否在活动时间内
            if (nowTime.compareTo(endTime) >= 0) {
                //缓存存在但过期的activityId，减少校验
                redisUtils.sadd(SET_UNACTIVITY, TIME_OUT << SET_UNACTIVITY_FACTOR, activityId);
                redisUtils.del(initActivityId);
                return false;
            }
            long expireTime = endTime.subtract(nowTime).longValue();
            if (redisUtils.hmset(activityHashKey, BeanUtil.beanToMap(one), expireTime)) {
                redisUtils.del(initActivityId);
                return true;
            }
            redisUtils.del(initActivityId);
            return false;
        } else {
            redisUtils.del(initActivityId);
            return false;
        }
    }
}
